package com.lanchetech.merchant.service.impl;


import com.lanchetech.bean.request.MerchantRegisterReq;
import com.lanchetech.bean.request.UpdatePasswordByPhoneReq;
import com.lanchetech.bean.response.BaseResp;
import com.lanchetech.common.enums.MerchantStatusEnum;
import com.lanchetech.common.enums.ToggleEnum;
import com.lanchetech.dao.MerchantMapper;
import com.lanchetech.dao.TenantMapper;
import com.lanchetech.entity.Merchant;
import com.lanchetech.entity.Tenant;
import com.lanchetech.merchant.config.JwtAuthenticationRequest;
import com.lanchetech.merchant.config.JwtTokenUtil;
import com.lanchetech.merchant.service.MerchantAuthService;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.common.enums.ResultCode;
import com.lanchetech.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.Date;

import static com.lanchetech.common.constants.JypConstant.*;

@Service
public class MerchantAuthServiceImpl implements MerchantAuthService {

    private AuthenticationManager authenticationManager;
    private UserDetailsService userDetailsService;
    private JwtTokenUtil jwtTokenUtil;
    private MerchantMapper merchantMapper;

    @Autowired
    SmsService smsService;

    @Autowired
    TenantMapper tenantMapper;

    @Autowired
    public MerchantAuthServiceImpl(
            AuthenticationManager authenticationManager,
            @Qualifier("customUserService") UserDetailsService userDetailsService,
            JwtTokenUtil jwtTokenUtil,
            MerchantMapper merchantMapper) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.merchantMapper = merchantMapper;
    }

    @Override
    public ResultData<String> login(JwtAuthenticationRequest req, HttpSession session) {
        ResultData<String> resultData = new ResultData<>();

        Boolean isValidateCaptcha = validateCaptcha(session.getAttribute("loginKey"), req.getCode());

        if (!isValidateCaptcha) {
            resultData.setResultCode(ResultCode.CAPTCHA_WRONG);
            session.removeAttribute("loginKey");
            return resultData;
        }


        try {
            UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(req.getUsername(), req.getPassword());
            final Authentication authentication = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            final UserDetails userDetails = userDetailsService.loadUserByUsername(req.getUsername());
            final String token = jwtTokenUtil.generateToken(userDetails);

            //找到此人对应的tenant，查看是否已经被关闭
            // 判断该管理员的总工会是否已关闭，关闭了就不能再登录了
            Merchant merchant = merchantMapper.findOneByUsername(req.getUsername());
            if (merchant != null && merchant.getTenantId() > 0) {
                Tenant tenant = tenantMapper.selectByPrimaryKey(merchant.getTenantId());
                if (ToggleEnum.OFF.getStatus().equals(tenant.getStatus())) {
                    resultData.setResultCode(ResultCode.TENANT_CLOSE);
                    return resultData;
                }
            }

            resultData.setValue(token);
            session.removeAttribute("loginKey");
        } catch (Exception e) {
            resultData.setResultCode(ResultCode.USERNAME_OR_PASSWORD_ERROR);
            session.removeAttribute("loginKey");
        }

        return resultData;
    }

    @Override
    public ResultData<String> getToken(JwtAuthenticationRequest req, HttpSession session) {
        ResultData<String> resultData = new ResultData<>();

        try {
            UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(req.getUsername(), req.getPassword());
            final Authentication authentication = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            final UserDetails userDetails = userDetailsService.loadUserByUsername(req.getUsername());
            final String token = jwtTokenUtil.generateToken(userDetails);

            //找到此人对应的tenant，查看是否已经被关闭
            // 判断该管理员的总工会是否已关闭，关闭了就不能再登录了
            Merchant merchant = merchantMapper.findOneByUsername(req.getUsername());
            if (merchant != null && merchant.getTenantId() > 0) {
                Tenant tenant = tenantMapper.selectByPrimaryKey(merchant.getTenantId());
                if (ToggleEnum.OFF.getStatus().equals(tenant.getStatus())) {
                    resultData.setResultCode(ResultCode.TENANT_CLOSE);
                    return resultData;
                }
            }

            resultData.setValue(token);
            session.removeAttribute("loginKey");
        } catch (Exception e) {
            resultData.setResultCode(ResultCode.USERNAME_OR_PASSWORD_ERROR);
            session.removeAttribute("loginKey");
        }

        return resultData;
    }

    @Override
    public BaseResp register(MerchantRegisterReq req) {
        Merchant merchant = merchantMapper.getOneByUsername(req.getUsername());
        // 验证码是否正确
        if (!smsService.verifySms(req.getPhone(), req.getCode())) {
            return new BaseResp(ResultCode.SMS_CODE_WRONG);
        }
        if (merchant != null) {
            return new BaseResp(ResultCode.MERCHANT_EXISTS);
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = req.getPassword();
        merchantMapper.register(Merchant.builder()
                .roleId(INIT_ROLE_ID)
                .username(req.getUsername())
                .password(encoder.encode(rawPassword))
                .nickname(req.getNickname())
                .avatar(MERCHANT_AVATAR)
                .lastPasswordResetDate(new Date())
                .status(MerchantStatusEnum.NORMAL.getStatus())
                .createdAt(new Date())
                .updatedAt(new Date())
                .tenantId(INIT_TENANT_ID)
                .registerName(req.getNickname())
                .phone(req.getPhone())
                .build());
        return new BaseResp();
    }

    @Override
    public BaseResp updatePasswordByPhone(UpdatePasswordByPhoneReq req) {
        Merchant merchant = merchantMapper.findOneByUsername(req.getUsername());

        if (merchant == null) {
            return new BaseResp(ResultCode.USER_NOT_EXISTS);
        }

        if (!smsService.verifySms(req.getPhone(), req.getCode())) {
            return new BaseResp(ResultCode.SMS_CODE_WRONG);
        }

        // 写入新密码
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = req.getNewPassword();
        merchant.setPassword(encoder.encode(rawPassword));
        merchant.setLastPasswordResetDate(new Date());
        merchant.setUpdatedAt(new Date());
        merchantMapper.updateByPrimaryKeySelective(merchant);

        return new BaseResp();
    }

    private Boolean validateCaptcha(Object captcha, String code) {
        Long towMinutesTime = 120000L;
        Integer codeLength = 4;

        if (captcha == null || code == null || code.length() != codeLength) {
            return false;
        }

        Long time = System.currentTimeMillis();
        String[] captchaArray = captcha.toString().split(",");
        Long captchaTime = new Long(captchaArray[0]);
        if (time > captchaTime + towMinutesTime) {
            return false;
        }

        return code.equals(captchaArray[1]);
    }
}